package com.opNaga.bpmp.flowObject;

import com.opNaga.bpmp.BPMPElement;
import com.opNaga.bpmp.artifact.Artifact;
import com.opNaga.bpmp.connectingObject.association.AssociationFlow;
import com.opNaga.bpmp.connectingObject.association.AssociationFlowSource;
import com.opNaga.bpmp.connectingObject.association.AssociationFlowTarget;
import com.opNaga.bpmp.swimlane.Containable;
import com.opNaga.bpmp.swimlane.Container;
import com.opNaga.bpmp.swimlane.HasRoot;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * User: Junyi BEI
 * Date: 19/01/2012
 * Time: 13:56
 */
public abstract class FlowObject extends BPMPElement implements Containable, AssociationFlowSource, AssociationFlowTarget, HasRoot {
  protected BPMPElement root;
  protected BPMPElement container;
  protected Set<BPMPElement> outwardAssociations = new HashSet<BPMPElement>();
  protected Set<BPMPElement> inwardAssociationFlows = new HashSet<BPMPElement>();

  public FlowObject() {
    super();
  }

  public FlowObject(String t) {
    super(t);
  }

  @Override
  public boolean destroy() {
    Iterator<BPMPElement> oaIt = iterateOutwardAssociationFlows();
    while(oaIt.hasNext()) {
      BPMPElement be = oaIt.next();
      if(!be.destroy()) {
        return standardMessage(800, be);
      }
    }
    Iterator<BPMPElement> iaIt = iterateInwardAssociationFlows();
    while(iaIt.hasNext()) {
      BPMPElement be = iaIt.next();
      if(!be.destroy()) {
        return standardMessage(801, be);
      }
    }
    if(((Container) container).removeElement(this)) {
      return standardMessage(830, container);
    }
    return super.destroy();
  }

  public boolean setContainer(BPMPElement be) {
    if(be == null) {
      container = be;
      root = be;
      return true;
    }
    if(!Container.class.isAssignableFrom(be.getClass())) {
      return standardMessage(570, be);
    }
    if(container != null) {
      return standardMessage(571, be, container);
    }
    container = be;
    root = ((Container) be).getRoot();
    if(Container.class.isAssignableFrom(getClass())) {
      Iterator<BPMPElement> beIt = ((Container) this).iterateElements();
      while(beIt.hasNext()) {
        BPMPElement cbe = beIt.next();
        if(!Containable.class.isAssignableFrom(cbe.getClass())) {
          return standardMessage(572, be, cbe);
        }
        if(!((Containable) cbe).setRoot(root)) {
          return standardMessage(572, be, cbe);
        }
      }
    }
    return standardMessage(592, be);
  }

  public BPMPElement getContainer() {
    return container;
  }

  public boolean addOutwardAssociationFlow(BPMPElement be) {
    if(!(be instanceof AssociationFlow)) {
      return standardMessage(120, be);
    }
    if(!outwardAssociations.add(be)) {
      return standardMessage(121, be);
    }
    return standardMessage(192, be);
  }

  public boolean removeOutwardAssociationFlow(BPMPElement be) {
    if(!(be instanceof AssociationFlow)) {
      return standardMessage(220, be);
    }
    if(!outwardAssociations.remove(be)) {
      return standardMessage(221, be);
    }
    return standardMessage(292, be);
  }

  public boolean isOutwardAssociationFlowEmpty() {
    return outwardAssociations.isEmpty();
  }

  public int outwardAssociationFlowSize() {
    return outwardAssociations.size();
  }

  public Iterator<BPMPElement> iterateOutwardAssociationFlows() {
    return new ArrayList<BPMPElement>(outwardAssociations).iterator();
  }

  public boolean addInwardAssociationFlow(BPMPElement be) {
    if(!(be instanceof AssociationFlow)) {
      return standardMessage(150, be);
    }
    if(!inwardAssociationFlows.add(be)) {
      return standardMessage(151, be);
    }
    return standardMessage(195, be);
  }

  public boolean removeInwardAssociationFlow(BPMPElement be) {
    if(!(be instanceof AssociationFlow)) {
      return standardMessage(250, be);
    }
    if(!inwardAssociationFlows.remove(be)) {
      return standardMessage(251, be);
    }
    return standardMessage(295, be);
  }

  public boolean isInwardAssociationFlowEmpty() {
    return inwardAssociationFlows.isEmpty();
  }

  public int inwardAssociationFlowSize() {
    return inwardAssociationFlows.size();
  }

  public Iterator<BPMPElement> iterateInwardAssociationFlows() {
    return new ArrayList<BPMPElement>(inwardAssociationFlows).iterator();
  }

  public BPMPElement getRoot() {
    return root;
  }

  public boolean setRoot(BPMPElement root) {
    if(Container.class.isAssignableFrom(getClass())) {
      Iterator<BPMPElement> beIt = ((Container) this).iterateElements();
      while(beIt.hasNext()) {
        BPMPElement be = beIt.next();
        if(!HasRoot.class.isAssignableFrom(be.getClass())) {
          return standardMessage(700, root, be);
        }
        if(!((HasRoot) be).setRoot(root)) {
          return standardMessage(700, root, be);
        }
      }
    }
    this.root = root;
    return standardMessage(740, root);
  }

  public BPMPElement linkTo(BPMPElement be) {
    if(AssociationFlowTarget.class.isAssignableFrom(be.getClass()) && Artifact.class.isAssignableFrom(be.getClass())) {
      BPMPElement af;
      if(manager != null) {
        if(be.getManager() == manager) {
          af = manager.createInstance("af", "-s=" + name, "-t=" + be.getName());
          if(af != null) {
            standardMessage(910, be, af);
            return af;
          } else {
            standardMessage(903, be);
            return null;
          }
        } else {
          standardMessage(902, be);
          return null;
        }
      } else {
        af = new AssociationFlow();
        if(!((AssociationFlow) af).setTarget(be)) {
          af.destroy();
          standardMessage(900, be, af);
          return null;
        }
        if(!((AssociationFlow) af).setSource(this)) {
          af.destroy();
          standardMessage(901, be, af);
          return null;
        }
        if(standardMessage(911, be, af)) {
          return af;
        }
      }
    }
    standardMessage(904, be);
    return null;
  }

  public BPMPElement getLinkTo(BPMPElement be) {
    if(AssociationFlowTarget.class.isAssignableFrom(be.getClass()) && Artifact.class.isAssignableFrom(be.getClass())) {
      Iterator<BPMPElement> oafIt = iterateOutwardAssociationFlows();
      while(oafIt.hasNext()) {
        BPMPElement oaf = oafIt.next();
        if(((AssociationFlow) oaf).getTarget() == be) {
          standardMessage(950, be, oaf);
          return oaf;
        }
      }
      standardMessage(940, be);
      return null;
    }
    standardMessage(941, be);
    return null;
  }

  public BPMPElement linkFrom(BPMPElement be) {
    if(AssociationFlowSource.class.isAssignableFrom(be.getClass()) && Artifact.class.isAssignableFrom(be.getClass())) {
      BPMPElement af;
      if(manager != null) {
        if(be.getManager() == manager) {
          af = manager.createInstance("af", "-s=" + be.getName(), "-t=" + name);
          if(af != null) {
            standardMessage(930, be, af);
            return af;
          } else {
            standardMessage(923, be);
            return null;
          }
        } else {
          standardMessage(922, be);
          return null;
        }
      } else {
        af = new AssociationFlow();
        if(!((AssociationFlow) af).setTarget(this)) {
          af.destroy();
          standardMessage(920, be, af);
          return null;
        }
        if(!((AssociationFlow) af).setSource(be)) {
          af.destroy();
          standardMessage(921, be, af);
          return null;
        }
        if(standardMessage(931, be, af)) {
          return af;
        }
      }
    }
    standardMessage(924, be);
    return null;
  }

  public BPMPElement getLinkFrom(BPMPElement be) {
    if(AssociationFlowSource.class.isAssignableFrom(be.getClass()) && Artifact.class.isAssignableFrom(be.getClass())) {
      Iterator<BPMPElement> iafIt = iterateInwardAssociationFlows();
      while(iafIt.hasNext()) {
        BPMPElement iaf = iafIt.next();
        if(((AssociationFlow) iaf).getSource() == be) {
          standardMessage(970, be, iaf);
          return iaf;
        }
      }
      standardMessage(960, be);
      return null;
    }
    standardMessage(961, be);
    return null;
  }
}
